#ifdef __KERNEL__

#include <asm-syscalls.h>
#include <atoms.h>
#include <kiddie/kiddie.h>
#include <Console.h>
#include <hw/Platform.h>
#include <sys/sched/Scheduler.h>
#include <virt/Jvm.h>
#include <mem/MemReader.h>

extern kernel::Console cout;
extern kernel::Platform hwplatform;
extern kernel::sys::sched::Scheduler *scheduler;

using util::strncmp;

#else
#include <string.h>
#include <iostream>
#include <virt/Jvm.h>
using std::cout;

void
__outl(u32 a, int b)
{
}
void
__outw(u32 a, int b)
{
}
void
__outb(u32 a, int b)
{
}
u32
__inl(int a)
{
  return 0;
}
u32
__inw(int a)
{
  return 0;
}
u32
__inb(int a)
{
  return 0;
}

#endif

#define DPRINTK(format,args...) do {} while (0)
// #define DPRINTK(format,args...)	printf("[kvm]"format,##args)
#undef JVM_TRACE
#define JVM_TRACE

namespace kernel
{
  namespace virt
  {
    namespace jvm
    {

      Jvm::Jvm()
      {
        sp = stack;

        ::cout << "JVM Constructor 1!\n";
      }

      Jvm::~Jvm()
      {
        sp = stack;
      }

      Jvm::Jvm(char *buf)
      {
        ::cout << "JVM Constructor 2!\n";
#ifdef __KERNEL__
        IReader *rdr = new ::kernel::mem::MemReader((vaddr_t)buf,0);
        clReader = new BEReader(rdr);
#endif
        parseByteCode();
      }

      /**
       * Create an array of 'len' elements
       */
      int
      Jvm::new_array(int len)
      {
        int arrayId; // array atom id
        int tmpB;

        arrayId = (int) (new VAtom());
        tmpB = arrayId;

        while (len-- > 0)
          {
            ((IAtom *) tmpB)->r = (int) (new VAtom());
            tmpB = ((IAtom *) tmpB)->r;
            ((IAtom *) tmpB)->ID = len;
          }

        return arrayId;
      }

      /**
       *
       */
      int
      Jvm::parseByteCode()
      {
        int i;
        int j;
        int k;
        int l;
        int m;
        int ccount;
        int fcount;
        int icount;
        int mcount;
        int acount;

        arrayList = NULL;

        // read class magic
        i = clReader->read32();
        // minor number
        j = clReader->read16();
        // major
        k = clReader->read16();

        // get constant pool size
        ccount = clReader->read16();

#ifdef JVM_TRACE
        //				::cout << (int)i << ":" << (int)k << "." << (int)j << "\n";
        //				::cout << ccount << " constants\n";
#endif
        if (ccount <= 1)
          return -1;

        // allocate constant pool space
        constPool = (cp_ref_t*) (new char[sizeof(cp_ref_t) * (ccount)]);

        // work on constants
        for (i = 1; i < ccount; i++)
          {
            // preallocate atoms
            constPool[i].AtomID = (int) (new VAtom());
          }

        // work on constants
        for (i = 1; i < ccount; i++)
          {
            int tmpA; // temporary atom ID
            cp_ref_t *pConst = &constPool[i];

            // read constant type
            pConst->type = clReader->read8();

            // set node type (classification)
            ((IAtom *) pConst->AtomID)->setVal(pConst->type);

#ifdef JVM_TRACE
            //							::cout << (int)i << ":	" << pConst->type << "\n";
#endif

            switch (pConst->type)
              {
            // UTF8 string
            case 1:
              // get byte length of the string
              pConst->id1 = clReader->read16();

              // allocate string holder
              pConst->id2 = (int) (new char[pConst->id1]);

              // copy string
              for (j = 0; j < pConst->id1; j++)
                ((char *) pConst->id2)[j] = clReader->read8();

              // Dup value in the Atom
              tmpA = (int) (new VAtom());
              ((IAtom *) tmpA)->setVal(pConst->id2);
              ((IAtom *) pConst->AtomID)->l = tmpA;

#ifdef JVM_TRACE
              ::cout << (int) i << ": " << pConst->AtomID << " (left) => "
                  << (char *) pConst->id2 << "\n";
#endif
              break;

              // 4 byte integer
            case 3:
            case 4:
              // read value
              pConst->id1 = clReader->read32();
              // Dup value in the Atom
              tmpA = (int) (new VAtom());
              ((IAtom *) tmpA)->setVal(pConst->id1);
              ((IAtom *) pConst->AtomID)->l = tmpA;
#ifdef JVM_TRACE
              ::cout << (int) i << ": " << pConst->AtomID << " (left) => "
                  << pConst->id1 << "\n";
#endif
              break;

              // 8 byte long
            case 5:
            case 6:
              // allocate holder
              pConst->id2 = (int) (new char[8]);
              // copy value
              *(int *) (pConst->id2 + 4) = clReader->read32();
              *(int *) pConst->id2 = clReader->read32();

              // allocate lowest level (subatomic) node
              tmpA = (int) (new VAtom());
              ((IAtom *) tmpA)->setVal(pConst->id2);
              ((IAtom *) pConst->AtomID)->l = tmpA;

#ifdef JVM_TRACE
              ::cout << (int) i << ": " << pConst->AtomID << " (left) => "
                  << pConst->id2 << "\n";
#endif

              break;

              // class ref
            case 7:
            case 8:
              pConst->id1 = clReader->read16();

              // link this node to others
              ((IAtom *) pConst->AtomID)->l = (constPool[pConst->id1].AtomID);
#ifdef JVM_TRACE
              ::cout << (int) i << ": " << pConst->AtomID << " => ["
                  << pConst->id1 << "(" << constPool[pConst->id1].AtomID
                  << ")]\n";
#endif
              break;

              // References
            case 10:
            case 9:
            case 11:
            case 12:
              pConst->id1 = clReader->read16();
              pConst->id2 = clReader->read16();

              ::cout << (int) i << ": " << pConst->AtomID << " => ["
                  << pConst->id1 << "(" << constPool[pConst->id1].AtomID
                  << ");" << pConst->id2 << "("
                  << constPool[pConst->id1].AtomID << ")]\n";

              // link this node to others
              ((IAtom *) pConst->AtomID)->l = (constPool[pConst->id1].AtomID);
              ((IAtom *) pConst->AtomID)->r = (constPool[pConst->id2].AtomID);
              break;

            default:
              // error
              ::cout << "Invalid constant type : " << pConst->type << "\n";
              return -1;
              }

          }

        // read Class flags
        i = clReader->read16();
        // read class name index
        j = clReader->read16();
        // read class super index
        k = clReader->read16();
        // read class interface number
        l = clReader->read16();

#ifdef JVM_TRACE
        //				::cout << (int)i << ":" << (int)k << "." << (int)j << "." << (int)l << "\n";
        ::cout << "\n\n.class " << (char *) constPool[constPool[j].id1].id2
            << "\n";
        ::cout << ".super " << (char *) constPool[constPool[k].id1].id2 << "\n";
#endif

        // skip interface descriptors
        // TODO
        clReader->jmp(l * 2);

        // get field number
        fcount = clReader->read16();
        fieldPool = (field_ref_t*) (new char[fcount * sizeof(field_ref_t)]);

#ifdef JVM_TRACE
        ::cout << "\n";
#endif

        for (i = 0; i < fcount; i++)
          {
            int tmpA; // temporary atom ID
            field_ref_t *pField = &fieldPool[i];

            // get field flags
            pField->acc_flags = clReader->read16();
            // read name index
            pField->id1 = clReader->read16();
            // read descriptor index
            pField->id2 = clReader->read16();
            // set field type from descriptor
            pField->type = *(char *) constPool[pField->id2].id2 & 0xff;

            // allocate temporary atom
            tmpA = (int) (new VAtom());
            ((IAtom *) tmpA)->l = constPool[pField->id1].AtomID;
            ((IAtom *) tmpA)->r = constPool[pField->id2].AtomID;
            ((IAtom *) tmpA)->setVal(pField->type);

            /* allocate new treenode */
            pField->AtomID = (int) (new VAtom());
            // store lvalue
            ((IAtom *) pField->AtomID)->l = tmpA;
            // set node type (classification) same as child node has
            ((IAtom *) pField->AtomID)->setVal(fieldtypes::FIELD);

#ifdef JVM_TRACE
            ::cout << ".field	" << (char *) constPool[pField->id1].id2;
            ::cout << "	" << (char *) constPool[pField->id2].id2;
#endif

            // find type and size of an array
            if (fieldtypes::REF == pField->type)
              {
                char *d = (char *) constPool[pField->id2].id2;

                j = 0;
                while (fieldtypes::REF == d[j++])
                  ;
                j--;
                pField->ref_dim = j;
                pField->ref_type = d[j];
              }

            // number of field attributes
            acount = clReader->read16();

            for (j = 0; j < acount; j++)
              {
                char *d;
                int name_idx;

                name_idx = clReader->read16();
                l = clReader->read32(); // read attr len
                d = (char *) constPool[name_idx].id2;

                if (!strncmp(d, "ConstantValue", 13))
                  {
                    // read constant value
                    l = clReader->read16();

                    switch (pField->type)
                      {
                    case fieldtypes::REF:
                      break;

                    default:
                      pField->val.i = constPool[l].id1;
#ifdef JVM_TRACE
                      ::cout << " = " << (int) pField->val.i;
#endif
                      break;
                      };
                  }
                else
                  {
                    clReader->jmp(l);
                  }
              }
#ifdef JVM_TRACE
            ::cout << "	; type " << pField->type << "\n";
#endif
          }

        // number of methods
        mcount = clReader->read16();
        // allocate function pool
        funcPool = (code_ref_t*) (new char[mcount * sizeof(code_ref_t)]);
#ifdef JVM_TRACE
        ::cout << "\n";
#endif
        // clear function array
        for (i = 0; i < mcount; i++)
          {
            code_ref_t *pMethod = &funcPool[i];
            pMethod->id0 = 0;
            pMethod->id1 = 0;
            pMethod->id2 = 0;
            pMethod->AtomID = 0;
          }

        // for each function
        for (i = 0; i < mcount; i++)
          {
            int tmpA; // temporary atom ID
            char *d;
            code_ref_t *pMethod = &funcPool[i];

            // get access flags
            pMethod->acc_flags = clReader->read16();
            // get name index
            pMethod->id1 = clReader->read16();
            // get descriptor id
            pMethod->id2 = clReader->read16();

            // allocate temporary atom
            tmpA = (int) (new VAtom());
            ((IAtom *) tmpA)->l = constPool[pMethod->id1].AtomID;
            ((IAtom *) tmpA)->r = constPool[pMethod->id2].AtomID;
            // FIXME This is default method type
            ((IAtom *) tmpA)->setVal('I');

#ifdef JVM_TRACE
            ::cout << ".method	";

            ::cout
                << ((pMethod->acc_flags & bits::ACC_PUBLIC) ? "public " : "");
            ::cout
                << ((pMethod->acc_flags & bits::ACC_STATIC) ? "static " : "");
            ::cout
                << ((pMethod->acc_flags & bits::ACC_NATIVE) ? "native " : "");

            ::cout << (char *) constPool[pMethod->id1].id2 << " "
                << (char *) constPool[pMethod->id2].id2 << "\n";
#endif

            /* allocate new treenode  for this method */
            if (!pMethod->AtomID)
              pMethod->AtomID = (int) (new VAtom());
            else
              {
                ::cout << "Func[" << i << "]=" << pMethod->AtomID << "\n";
              }
            // store lvalue
            ((IAtom *) pMethod->AtomID)->l = tmpA;
            // set node type (classification) same as child node has
            ((IAtom *) pMethod->AtomID)->setVal(fieldtypes::METHOD);

            // get attributes count
            acount = clReader->read16();

#ifdef JVM_TRACE
            // ::cout << acount << " attributes\n";
#endif

            // parse method attributes....
            for (j = 0; j < acount; j++)
              {
                char *d;
                int name_idx;

                // raad name index
                name_idx = clReader->read16();
                // read attribute length
                l = clReader->read32();
                // get attribute
                d = (char *) constPool[name_idx].id2;

                // clReader->jmp(l);
                if (!strncmp(d, "Code", 4))
                  {
                    // read max stack
                    pMethod->max_stack = clReader->read16();
                    // read max locals
                    pMethod->max_locals = clReader->read16();
                    // read code length
                    l = clReader->read32();

#ifdef JVM_TRACE
                    ::cout << ".limit locals " << pMethod->max_stack << "\n";
                    ::cout << ".limit stack " << pMethod->max_locals << "\n";
#endif
                    // read code
                    /* TODO Don't use allocator here, parse the code by reading bytes */
                    d = new char[l];
                    for (k = 0; k < l; k++)
                      d[k] = clReader->read8();

                    // set current atom ID
                    this->curAtom = pMethod->AtomID;
                    parseCode(d, l, 0);
                    delete[] d;

                    // exception table length
                    l = clReader->read16();
                    clReader->jmp(l * 8);

                    // attributes count
                    l = clReader->read16();
                    // ignore code attributes
                    for (m = 0; m < l; m++)
                      {
                        clReader->read16();
                        clReader->jmp(clReader->read32()); // read attr len
                      }
#ifdef JVM_TRACE
                    ::cout << ".end method\n\n";
#endif
                  }
                else
                  {
                    clReader->jmp(l);
                  }
              }

            // find maximum number of params
            d = (char *) constPool[pMethod->id2].id2;
            l = constPool[pMethod->id2].id1;
            pMethod->max_params = parse_max_paramnum(d, l);

          }

        acount = clReader->read16();
#ifdef JVM_TRACE
        ::cout << "\n";
        ::cout << acount << " attributes\n";
#endif

        reset();
        return 0;
      }

      void
      Jvm::reset(void)
      {
        sp = stack;
        sp += funcPool[0].max_locals;
        mID = 0;
      }

      int
      Jvm::parse_max_paramnum(char *s, int len)
      {
        int i = 0, j = len;
        if (len <= 3)
          {
            return 0;
          }
        for (; j-- > 0 && *s && *s != '('; s++)
          ;

        for (; j-- > 0 && *s && *s != ')'; s++)
          {
            switch (*s)
              {
            case fieldtypes::D:
            case fieldtypes::J:
            case fieldtypes::B:
            case fieldtypes::C:
            case fieldtypes::F:
            case fieldtypes::I:
            case fieldtypes::L:
            case fieldtypes::S:
            case fieldtypes::Z:
              i++;
            case fieldtypes::REF:
            default:
              continue;
              }
          }

        return i;
      }

      // bytecode parser / IR generator
      void
      Jvm::parseCode(char *pc, int len, int *refs)
      {
        bool stop = false;
        code_ref_t *pMethod;
        cp_ref_t *pConst;
        int tmpA; // temp atom ID
        int tmpB; // temp atom ID
        int zeroRef;
        int lval; // lvalue ID
        int rval; // rvalue ID
        u8 v;
        u8 pc_inc;
        vm_arg_t arg0;
        char *cpc = pc; // current pc
        int *references = refs;
        int i = 0;

        int *lv;
        // FIXME The size of register file \
				// must be taken from 'max_locals'
        lv = new int[10];
        for (i = 0; i < 10; i++)
          {
            // preallocate local atoms
            lv[i] = (int) (new VAtom());
          }

        i = 0;

        // FIXME Make this global variable
        if (!references)
          references = new int[len];

        // Prepare next cycle
        // allocate temporary atom
        tmpA = (int) (new VAtom());
        ((IAtom *) curAtom)->r = tmpA;
        curAtom = tmpA;

        // allocate treenode for opcode
        tmpA = (int) (new VAtom());
        ((IAtom *) curAtom)->l = tmpA;

        zeroRef = (int) (new VAtom());
        ((IAtom *) zeroRef)->setVal(0);
        ((IAtom *) zeroRef)->l = 0;
        ((IAtom *) zeroRef)->r = 0;

        do
          {
            v = *(u8*) cpc;
            arg0.z.bh = *(char *) ((size_t) cpc + 1);
            arg0.z.bl = *(char *) ((size_t) cpc + 2);

            pc_inc = 1;
            ::cout << "	";

            // save Vatom reference
            references[(int) (cpc - pc)] = tmpA;
            // FIXME This is default method type
            ((IAtom *) curAtom)->setVal(fieldtypes::OPCODE);

            // set its value to real java opcode
            ((IAtom *) tmpA)->setVal(v);

#ifdef JVM_TRACE
            ::cout << i++ << "(" << (int) (cpc - pc) << ") [" << tmpA << "]: ";
#endif

            // #undef JVM_TRACE
            switch (v)
              {
            case opcodes::NOP:
              continue;
#ifdef JVM_TRACE
              ::cout << "nop\n";
#endif
              break;

            case opcodes::BIPUSH:
              pc_inc = 2;
              arg0.z.bh = *(char *) ((size_t) cpc + 1);

              // allocate treenode for values
              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->setVal((int) arg0.z.bh & 0xff);
              ((IAtom *) tmpB)->l = 0;
              ((IAtom *) tmpB)->r = 0;

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);
              push(tmpB);

#ifdef JVM_TRACE
              ::cout << "bipush #" << (int) ((IAtom *) tmpB)->getVal() << "\n";
#endif
              continue;
              break;

            case opcodes::SIPUSH:
              pc_inc = 3;
              arg0.z.bh = *(char *) ((size_t) cpc + 1);
              arg0.z.bl = *(char *) ((size_t) cpc + 2);

              // allocate treenode for each value
              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->setVal((int) arg0.w & 0xff);
              ((IAtom *) tmpB)->l = 0;
              ((IAtom *) tmpB)->r = 0;

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);
              push(tmpB);

#ifdef JVM_TRACE
              ::cout << "sipush #" << (int) ((IAtom *) tmpB)->getVal() << "\n";
#endif
              continue;
              break;

            case opcodes::ICONST_M1:
            case opcodes::ICONST_0:
            case opcodes::ICONST_1:
            case opcodes::ICONST_2:
            case opcodes::ICONST_3:
            case opcodes::ICONST_4:
            case opcodes::ICONST_5:

              // allocate treenode for each value
              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->setVal((int) (v - opcodes::ICONST_0));
              ((IAtom *) tmpB)->l = 0;
              ((IAtom *) tmpB)->r = 0;

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);
              push(tmpB);

#ifdef JVM_TRACE
              ::cout << "iconst_" << (int) ((IAtom *) tmpB)->getVal() << "\n";
#endif
              continue;
              break;

            case opcodes::LCONST_0:
              // allocate treenode for each value
              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->setVal((int) 0);
              ((IAtom *) tmpB)->l = 0;
              ((IAtom *) tmpB)->r = 0;

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);
              push(tmpB);

#ifdef JVM_TRACE
              ::cout << "lconst_0\n";
#endif
              continue;
              break;

            case opcodes::LCONST_1:
              // allocate treenode for each value
              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->setVal((int) 1);
              ((IAtom *) tmpB)->l = 0;
              ((IAtom *) tmpB)->r = 0;

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);
              push(tmpB);

#ifdef JVM_TRACE
              ::cout << "lconst_1\n";
#endif
              continue;
              break;

            case opcodes::FCONST_0:
            case opcodes::FCONST_1:
            case opcodes::FCONST_2:
              // allocate treenode for each value
              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->setVal((int) (v - opcodes::FCONST_0));
              ((IAtom *) tmpB)->l = 0;
              ((IAtom *) tmpB)->r = 0;

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);
              push(tmpB);

#ifdef JVM_TRACE
              ::cout << "fconst_" << (int) ((IAtom *) tmpB)->getVal() << "\n";
#endif
              continue;
              break;

            case opcodes::FSTORE:
              pc_inc = 2;

              arg0.z.bh = *(char *) ((size_t) cpc + 1);

              // allocate treenode for each value
              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->setVal((int) arg0.z.bh & 0xff);
              ((IAtom *) tmpB)->l = 0;
              ((IAtom *) tmpB)->r = 0;

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::MOV);

              // set rvalue to tmpB (where to store)
              ((IAtom *) tmpA)->r = tmpB;

              // get result reference from stack
              tmpB = pop();
              // set lvalue to tmpB (what to store)
              ((IAtom *) tmpA)->l = tmpB;

#ifdef JVM_TRACE
              ::cout << "fstore " << (int) (arg0.z.bh) << "\n";
#endif
              break;

            case opcodes::ASTORE:
            case opcodes::ISTORE:
              pc_inc = 2;
              arg0.z.bh = *(char *) ((size_t) cpc + 1);

              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::MOV);

              // set lvalue to tmpB (what to store)
              ((IAtom *) tmpA)->l = pop();

              // set rvalue to tmpB (where to store)
              ((IAtom *) tmpA)->r = lv[(int) arg0.z.bh & 0xff];

#ifdef JVM_TRACE
              ::cout << "astore " << (int) arg0.z.bh << "\n";
#endif
              break;

            case opcodes::ASTORE_0:
            case opcodes::ASTORE_1:
            case opcodes::ASTORE_2:
            case opcodes::ASTORE_3:
              // set opcode
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::MOV);

              // set lvalue to tmpB (what to store)
              ((IAtom *) tmpA)->l = pop();

              // set rvalue to tmpB (where to store)
              ((IAtom *) tmpA)->r = lv[(int) (v - opcodes::ASTORE_0)];

#ifdef JVM_TRACE
              ::cout << "astore_" << (int) (v - opcodes::ASTORE_0) << "\n";
#endif
              break;

            case opcodes::ISTORE_0:
            case opcodes::ISTORE_1:
            case opcodes::ISTORE_2:
            case opcodes::ISTORE_3:
              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::MOV);

              // set lvalue to tmpB (what to store)
              ((IAtom *) tmpA)->l = pop();

              // set rvalue to tmpB (where to store)
              ((IAtom *) tmpA)->r = lv[(int) (v - opcodes::ISTORE_0)];

#ifdef JVM_TRACE
              ::cout << "istore_" << (int) (v - opcodes::ISTORE_0) << "\n";
#endif
              break;

            case opcodes::IASTORE:
            case opcodes::SASTORE:
            case opcodes::BASTORE:
            case opcodes::CASTORE:
            case opcodes::AASTORE:

              // set rvalue to Value reference
              ((IAtom *) tmpA)->r = pop();

              // allocate treenode for each value
              tmpB = pop(); // index AtomID
              lval = ((IAtom *) tmpB)->getVal(); // take index

              tmpB = pop(); // arrayref AtomID

              tmpB = (int) ((IAtom *) tmpB)->getById(lval);
              if (tmpB == 0)
                {
                  ::cout << "ERROR Retreiving array element " << lval << "\n";
                  tmpB = (int) (new VAtom());
                  ((IAtom *) tmpB)->setVal(0);
                  ((IAtom *) tmpB)->l = 0;
                  ((IAtom *) tmpB)->r = 0;
                }

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::MOV);
              ((IAtom *) tmpA)->l = tmpB;

#ifdef JVM_TRACE
              ::cout << "astore " << tmpB << "[" << lval << "]\n";
#endif
              break;

            case opcodes::AALOAD:
            case opcodes::IALOAD:
            case opcodes::SALOAD:
            case opcodes::BALOAD:
            case opcodes::CALOAD:

              tmpB = pop();
              tmpB = pop();
              push(tmpB);

#ifdef JVM_TRACE
              ::cout << "aaload\n";
#endif
              break;

            case opcodes::ILOAD:
              pc_inc = 2;

              arg0.z.bh = *(char *) ((size_t) cpc + 1);

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);

              // allocate treenode for each value
              tmpB = lv[(int) arg0.z.bh & 0xff];

              push(tmpB);

#ifdef JVM_TRACE
              ::cout << "iload " << (int) (0xff & arg0.z.bh) << "\n";
#endif
              continue;
              break;

            case opcodes::ILOAD_0:
            case opcodes::ILOAD_1:
            case opcodes::ILOAD_2:
            case opcodes::ILOAD_3:

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);

              // allocate treenode for each value
              tmpB = lv[(int) (v - opcodes::ILOAD_0)];
              push(tmpB);

#ifdef JVM_TRACE
              ::cout << "iload_" << (int) (v - opcodes::ILOAD_0) << "\n";
#endif
              continue;
              break;

            case opcodes::ALOAD:
              pc_inc = 2;
              arg0.z.bh = *(char *) ((size_t) cpc + 1);

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);

              // allocate treenode for each value
              tmpB = lv[(int) arg0.z.bh & 0xff];

              push(tmpB);

#ifdef JVM_TRACE
              ::cout << "aload " << (int) (arg0.z.bh) << "\n";
#endif
              continue;
              break;

            case opcodes::ALOAD_0:
            case opcodes::ALOAD_1:
            case opcodes::ALOAD_2:
            case opcodes::ALOAD_3:

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);

              // allocate treenode for each value
              tmpB = lv[(int) (v - opcodes::ALOAD_0)];

              push(tmpB);

#ifdef JVM_TRACE
              ::cout << "aload_" << (int) (v - opcodes::ALOAD_0) << "\n";
#endif
              continue;
              break;

            case opcodes::IFEQ:
            case opcodes::IFNE:
            case opcodes::IFLT:
            case opcodes::IFGE:
            case opcodes::IFLE:
            case opcodes::IF_ICMPEQ:
            case opcodes::IF_ICMPNE:
            case opcodes::IF_ICMPLT:
            case opcodes::IF_ICMPGT:
            case opcodes::IF_ICMPLE:
            case opcodes::IF_ICMPGE:
              pc_inc = 3;
              arg0.z.bh = *(char *) ((size_t) cpc + 1);
              arg0.z.bl = *(char *) ((size_t) cpc + 2);

              // allocate treenode for each branch
              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->setVal(kernel::virt::common::opcodes::LT);
              ((IAtom *) tmpB)->l = 0; // operation code
              ((IAtom *) tmpB)->r = 0; // branch

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::IF);
              ((IAtom *) tmpA)->l = tmpB;

#ifdef JVM_TRACE
              ::cout << "if <cond> then goto " << (int) arg0.w << "\n";
#endif

              // save curAtom
              push( curAtom);
              // set curAtom to tmpA
              curAtom = tmpA;
#ifdef JVM_TRACE
              ::cout << "push curAtom = " << curAtom << "\n";
#endif

              // we already know the Vatom reference
              if (arg0.w <= 0) // cicle
                {
                  // take Vatom reference
                  tmpB = references[((int) (cpc - pc) + arg0.w)];
#ifdef JVM_TRACE
                  ::cout << ((int) (cpc - pc) + arg0.w) << "[" << tmpB << "]: ";
#endif
                  // set lvalue of current position Atom
                  // (it differs from tmpA atom, and is its
                  // parent) to tmpB
                  ((IAtom *) tmpA)->r = tmpB;
                }
              else // fw branch
                {
                  parseCode(cpc + (int) arg0.w, pc + len - cpc - (int) arg0.w,
                      references);
                }

              // restore curAtom
              curAtom = pop();

#ifdef JVM_TRACE
              ::cout << "pop curAtom = " << curAtom << "\n";
#endif
              break;

            case opcodes::GOTO:
              pc_inc = 3;
              arg0.z.bh = *(char *) ((size_t) cpc + 1);
              arg0.z.bl = *(char *) ((size_t) cpc + 2);

              ((IAtom *) tmpA)->setVal(opcodes::NOP);

              // we already know the Vatom reference
              if (arg0.w <= 0) // uncondition loop
                {
                  // take Vatom reference
                  tmpB = references[((int) (cpc - pc) + arg0.w)];
                  ::cout << ((int) (cpc - pc) + arg0.w) << "[" << tmpB << "]: ";
                  // set lvalue of current position Atom
                  // (it differs from tmpA atom, and is its
                  // parent) to tmpB
                  ((IAtom *) curAtom)->r = tmpB;
                  stop = true;
                }
              else // uncondition fw branch
                {
                  cpc += ((int) arg0.w - 3);
                }
#ifdef JVM_TRACE
              ::cout << "goto " << (int) arg0.w << "\n";
#endif
              continue;
              break;

            case opcodes::INEG:
              // Set opcode to SUBTRACT: -b = 0 - b,
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::SUB);

              // allocate treenode for left stack operand
              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->l = zeroRef;
              ((IAtom *) tmpB)->r = pop();
              ((IAtom *) tmpA)->l = tmpB;

              // allocate treenode for right stack operand
              tmpB = (int) (new VAtom()); // result reference
              ((IAtom *) tmpA)->r = tmpB;
              push(tmpB);

#ifdef JVM_TRACE
              ::cout << "ineg\n";
#endif
              break;

            case opcodes::IINC:
              pc_inc = 3;
              arg0.z.bh = *(char *) ((size_t) cpc + 1);
              arg0.z.bl = *(char *) ((size_t) cpc + 2);

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::ADD);

              // allocate treenode for regnum
              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->setVal(kernel::virt::common::opcodes::ADD);
              ((IAtom *) tmpB)->l = lv[(int) arg0.z.bh & 0xff]; // operation code

              // allocate new atom for arg0.z.bl
              lval = (int) (new VAtom());
              ((IAtom *) lval)->l = 0;
              ((IAtom *) lval)->r = 0;
              ((IAtom *) lval)->setVal((int) arg0.z.bl);

              ((IAtom *) tmpB)->r = lval; // branch

              ((IAtom *) tmpA)->l = tmpB;

              // store to the same atom
              ((IAtom *) tmpA)->r = lv[(int) arg0.z.bh & 0xff];

#ifdef JVM_TRACE
              ::cout << "iinc " << (int) (arg0.z.bl) << "\n";
#endif
              break;

            case opcodes::IADD:
              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::ADD);

              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->setVal(kernel::virt::common::opcodes::ADD);
              ((IAtom *) tmpB)->r = pop();
              ((IAtom *) tmpB)->l = pop();

              ((IAtom *) tmpA)->l = tmpB;

              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->r = 0;
              ((IAtom *) tmpB)->l = 0;

              ((IAtom *) tmpA)->r = tmpB;

              // allocate treenode for left stack operand
              push(tmpB);

#ifdef JVM_TRACE
              ::cout << "iadd " << ((IAtom *) tmpA)->l << "+"
                  << ((IAtom *) tmpA)->r << "\n";
#endif
              break;

            case opcodes::ISUB:

              // set lvalue to tmpB
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::SUB);

              // allocate treenode for left stack operand
              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->r = pop();
              ((IAtom *) tmpB)->l = pop();
              ((IAtom *) tmpA)->l = tmpB;

              // allocate treenode for right stack operand
              tmpB = (int) (new VAtom()); // result reference
              ((IAtom *) tmpA)->r = tmpB;
              push(tmpB);
#ifdef JVM_TRACE
              ::cout << "isub\n";
#endif
              break;

            case opcodes::IMUL:
#ifdef JVM_TRACE
              ::cout << "iadd\n";
#endif
              continue;
              break;

            case opcodes::IDIV:
#ifdef JVM_TRACE
              ::cout << "iadd\n";
#endif
              continue;
              break;

            case opcodes::IREM:
#ifdef JVM_TRACE
              ::cout << "iadd\n";
#endif
              continue;
              break;

            case opcodes::ISHL:
#ifdef JVM_TRACE
              ::cout << "iadd\n";
#endif
              continue;
              break;

            case opcodes::ISHR:
#ifdef JVM_TRACE
              ::cout << "iadd\n";
#endif
              continue;
              break;

            case opcodes::IAND:
#ifdef JVM_TRACE
              ::cout << "iadd\n";
#endif
              continue;
              break;

            case opcodes::IOR:
#ifdef JVM_TRACE
              ::cout << "iadd\n";
#endif
              continue;
              break;

            case opcodes::IXOR:
#ifdef JVM_TRACE
              ::cout << "iadd\n";
#endif
              continue;
              break;

            case opcodes::IUSHR:
#ifdef JVM_TRACE
              ::cout << "iadd\n";
#endif
              continue;
              break;

            case opcodes::I2B:
#ifdef JVM_TRACE
              ::cout << "i2b\n";
#endif
              continue;
              break;

            case opcodes::POP2:
#ifdef JVM_TRACE
              ::cout << "pop2\n";
#endif
              continue;
              break;

            case opcodes::POP:
              pop();
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);
#ifdef JVM_TRACE
              ::cout << "pop\n";
#endif
              continue;
              break;

            case opcodes::DUP:
              tmpB = pop();
              push(tmpB);
              push(tmpB);
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);
#ifdef JVM_TRACE
              ::cout << "dup\n";
#endif
              continue;
              break;

            case opcodes::DUP2:
#ifdef JVM_TRACE
              ::cout << "dup2\n";
#endif
              continue;
              break;

            case opcodes::GETSTATIC:
              pc_inc = 3;
              arg0.z.bh = *(char *) ((size_t) cpc + 1);
              arg0.z.bl = *(char *) ((size_t) cpc + 2);
#ifdef JVM_TRACE
              ::cout << "getstatic #" << (int) arg0.w << "\n";
#endif
              continue;
              break;

            case opcodes::PUTSTATIC:
              pc_inc = 3;

              arg0.z.bh = *(char *) ((size_t) cpc + 1);
              arg0.z.bl = *(char *) ((size_t) cpc + 2);

#ifdef JVM_TRACE
              ::cout << "putstatic #" << (int) arg0.w << " -> ?\n";
#endif
              continue;
              break;

            case opcodes::LDC:
              pc_inc = 2;
              arg0.z.bh = *(char *) ((size_t) cpc + 1);
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);
              tmpB = constPool[arg0.z.bh].AtomID;
              tmpB = ((IAtom *) tmpB)->l;
              push(tmpB);
#ifdef JVM_TRACE
              ::cout << "ldc #" << (int) arg0.z.bh << "\n";
#endif
              continue;
              break;

              /**
               * TODO Method invocation must be cleaned
               */
            case opcodes::INVOKEVIRTUAL:
            case opcodes::INVOKESTATIC:
            case opcodes::INVOKESPECIAL:
              pc_inc = 3;
              arg0.z.bh = *(char *) ((size_t) cpc + 1);
              arg0.z.bl = *(char *) ((size_t) cpc + 2);

              pConst = &constPool[(int) (arg0.w)];
              tmpB = pConst->AtomID;

#ifdef JVM_TRACE
              ::cout << "invoke  #" << (int) (arg0.w) << " : " << tmpB
                  << " => " << ((IAtom *) tmpB)->l << ";"
                  << ((IAtom *) tmpB)->r << "\n";
#endif
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::INVOKE);
              ((IAtom *) tmpA)->l = tmpB;
              break;

            case opcodes::GETFIELD:
              pc_inc = 3;
              arg0.z.bh = *(char *) ((size_t) cpc + 1);
              arg0.z.bl = *(char *) ((size_t) cpc + 2);

              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);

              // take objectref from stack
              tmpB = pop();

              // allocate treenode for each value
              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->setVal((int) arg0.w); // value

              // store const reference into stack
              push(tmpB);
#ifdef JVM_TRACE
              ::cout << "getfield #" << (int) arg0.w << "\n";
#endif
              continue;
              break;

            case opcodes::PUTFIELD:
              pc_inc = 3;
              arg0.z.bh = *(char *) ((size_t) cpc + 1);
              arg0.z.bl = *(char *) ((size_t) cpc + 2);

              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::MOV);

              // allocate treenode for each value
              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->setVal(pop()); // value
              // set lvalue to tmpB
              ((IAtom *) tmpA)->l = tmpB;

              // allocate treenode for each value
              tmpB = (int) (new VAtom());
              ((IAtom *) tmpB)->setVal((int) arg0.w); // value
              // set lvalue to tmpB
              ((IAtom *) tmpA)->r = tmpB;

#ifdef JVM_TRACE
              ::cout << "putfield #" << (int) arg0.w << "\n";
#endif
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::MOV);
              break;

            case opcodes::NEW:
              pc_inc = 3;
#ifdef JVM_TRACE
              ::cout << "new #" << (int) (0xffff & arg0.w) << "\n";
#endif
              continue;
              break;

            case opcodes::NEWARRAY:
              pc_inc = 2;
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::NOP);
              tmpB = pop(); // number of elements
              lval = ((IAtom *) tmpB)->getVal();
              tmpB = new_array(lval);
              push(tmpB);
#ifdef JVM_TRACE
              ::cout << "newarray " << tmpB << "[" << (int) lval << "]\n";
#endif
              continue;
              break;

            case opcodes::ARRAYLENGTH:
#ifdef JVM_TRACE
              ::cout << "arraylength\n";
#endif
              continue;
              break;

            case opcodes::ACONST_NULL:
#ifdef JVM_TRACE
              ::cout << "aconst_null\n";
#endif
              continue;
              break;

            case opcodes::IRETURN:
            case opcodes::LRETURN:
            case opcodes::FRETURN:
            case opcodes::DRETURN:
            case opcodes::ARETURN:
            case opcodes::RETURN:
              ((IAtom *) tmpA)->setVal(kernel::virt::common::opcodes::RETURN);
#ifdef JVM_TRACE
              ::cout << "return " << (int) (arg0.z.bh) << "\n";
#endif
              break;
              }

            // Prepare next cycle
            // allocate temporary atom
            tmpA = (int) (new VAtom());
            ((IAtom *) curAtom)->r = tmpA;
            curAtom = tmpA;

            // allocate treenode for opcode
            tmpA = (int) (new VAtom());
            ((IAtom *) curAtom)->l = tmpA;

          }
        while (cpc += pc_inc, !stop && ((v < opcodes::IRETURN) || (v
            > opcodes::RETURN)));
      }

      void
      Jvm::push(int val)
      {
        *(++sp) = val;
      }

      int
      Jvm::pop(void)
      {
        return *(sp--);
      }

      //
      int
      Jvm::putfield(int idx, int val)
      {
        int i = idx;
        if (consttypes::FIELDREF == constPool[i].type)
          {
            fieldPool[constPool[i].id1].val.i = (int) val;
            return 0;
          }
        else
          {
            ::cout << "-ERROR!!! Invalid field reference\n";
            return -1;
          }
      }

      int
      Jvm::getfield(int idx)
      {
        int val;
        int i = idx;
        if (consttypes::FIELDREF == constPool[i].type)
          {
            val = (int) fieldPool[constPool[i].id1].val.i;
            return val;
          }
        else
          {
            ::cout << "-ERROR!!! Invalid field reference\n";
          }
        return 0;
      }

#define JVM_TRACE
      // Atom chain interpreter
      void
      Jvm::runAtoms(int ID)
      {
        int _id;
        int __id;
        int val1;
        int val2;
        vm_arg_t arg0;
        int tmpL;
        int tmpR;

        int i = 0; // counter

        ::cout << "Running method ATOM ID => " << ID << "\n";

        i = 0;

        for (_id = ID; _id; _id = ((IAtom *) _id)->r)
          {

#ifdef JVM_TRACE
            ::cout << i++ << ":	";
            //					::cout << "atom ref: " << _id << " : ";
#endif
            // get lvalue
            __id = ((IAtom *) _id)->l;

            //	::cout << _id << "->l => " << __id << "\n";
            //		continue;
            int v = ((IAtom *) __id)->getVal();
            arg0.w = (s16)((IAtom *) __id)->l;
            tmpL = ((IAtom *) __id)->l;
            tmpR = ((IAtom *) __id)->r;

            switch (v)
              {

            case kernel::virt::common::opcodes::MOV:
              val1 = tmpL;
              val2 = tmpR;
#ifdef JVM_TRACE
              ::cout << "mov " << tmpL << " => " << tmpR;
#endif
              ((IAtom *) tmpR)->setVal(((IAtom *) tmpL)->getVal());
              break;

            case kernel::virt::common::opcodes::IF:
              // val1 = ((IAtom *) tmpR)->getVal();
              val1 = tmpR;
#ifdef JVM_TRACE
              ::cout << "if <cond> then goto " << val1 << " (" << tmpR << ")";
#endif
              _id = __id;
              break;

            case kernel::virt::common::opcodes::ADD:
              val1 = ((IAtom *) tmpL)->l;
              val2 = ((IAtom *) tmpL)->r;
              val1 = ((IAtom *) val1)->getVal();
              val2 = ((IAtom *) val2)->getVal();
#ifdef JVM_TRACE
              ::cout << "" << val1 << " + " << val2 << " = " << (int) (val1
                  + val2);
#endif
              ((IAtom *) tmpR)->setVal(val1 + val2);
              break;

            case kernel::virt::common::opcodes::SUB:
              val1 = ((IAtom *) tmpL)->l;
              val2 = ((IAtom *) tmpL)->r;
              val1 = ((IAtom *) val1)->getVal();
              val2 = ((IAtom *) val2)->getVal();
#ifdef JVM_TRACE
              ::cout << "" << val1 << " - " << val2 << " = " << (int) (val1
                  - val2);
#endif
              ((IAtom *) tmpR)->setVal(val1 - val2);
              break;

              /**
               * TODO Method invocation must be cleaned
               */
            case kernel::virt::common::opcodes::INVOKE:
              val1 = ((IAtom *) tmpL)->l;
              val1 = ((IAtom *) val1)->l;
              val1 = ((IAtom *) val1)->l;
              val2 = ((IAtom *) tmpL)->r;
              val2 = ((IAtom *) val2)->l;
              val2 = ((IAtom *) val2)->l;

#ifdef JVM_TRACE
              ::cout << "call " << (char *) ((IAtom *) val1)->getVal() << "::"
                  << (char *) ((IAtom *) val2)->getVal();
#endif
              break;

            case opcodes::NEW:
#ifdef JVM_TRACE
              ::cout << "new #" << (int) (0xffff & arg0.w);
#endif
              break;

            case opcodes::ARRAYLENGTH:
#ifdef JVM_TRACE
              ::cout << "arraylength";
#endif
              break;

            case kernel::virt::common::opcodes::RETURN:
#ifdef JVM_TRACE
              ::cout << "return " << (int) (arg0.z.bh);
#endif
              break;
              } // switch case
#ifndef __KERNEL__
            ::cout << "\n" << ::std::flush;
#endif
          } // for (_id = ID; _id; _id = ((IAtom *) _id)->r)
      }

    } // namespace jvm
  } // namespace virt
} // namespace kernel


#ifndef __KERNEL__

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
//#include <stdlib>


namespace test
{

  class FileReader : public Reader
  {
    int fd;

  public:
    FileReader(char *fname)
    {
      fd = ::open(fname, O_RDWR);
    }

    ~FileReader()
    {
      ::close(fd);
    }

    uint32_t
    read32blk(size_t off)
    {
      unsigned int r0;
      unsigned int r1;
      r1 = off & ~0x3;

      ::lseek(fd, r1, SEEK_SET);
      ::read(fd, &r0, sizeof(unsigned int));

      return r0;
    }

  };

}

/**
 * to compile run
 *  gcc -o nanollvm Llvm.cpp ../../util/string.cpp ../Object.cpp -I../../../include/ -lstdc++
 *
 */
int
main(int argc, char **argv)
{
  IReader *rdr;
  struct stat f_stat;
  char *buf;
  int fd, len;

  kernel::virt::jvm::Jvm *vm = new kernel::virt::jvm::Jvm();

  rdr = new ::test::FileReader(argv[1]);
  vm->clReader = new BEReader(rdr);

  vm->parseByteCode();
  vm->runAtoms(vm->funcPool[0].AtomID);
}

#endif

